package main

import (
	"bytes"
	"fmt"
	"os"
	"syscall"
	"unsafe"

	io_uring "gitee.com/childewang/iouring-go"
	iouring_syscall "gitee.com/childewang/iouring-go/syscall"
)

const entries uint = 16
const blockSize int64 = 32 * 1024

type ioData struct {
	read        int
	firstOffset uint64
	offset      uint64
	firstLen    uint64
	iov         []syscall.Iovec
}

func queueRead(iouring *io_uring.IOUring, size uint64, offset uint64, infd int) bool {
	data := new(ioData)

	sqe := iouring.GetSQE()
	if sqe == nil {
		return false
	}

	data.read = 1
	data.offset = offset
	data.firstOffset = offset

	bs := new([]byte)

	iovs := []syscall.Iovec{
		{Base: (*byte)(unsafe.Pointer(bs)),
			Len: uint64(size)},
	}
	data.iov = iovs
	data.firstLen = size

	fmt.Printf("data: %v\n", data)
	fmt.Println("data: ", uint64(uintptr(unsafe.Pointer(data))))

	sqe.PrepReadv(infd, iovs, 1, int64(offset))
	sqe.SetData(uint64(uintptr(unsafe.Pointer(data))))

	return true
}

func write(iouring *io_uring.IOUring) {
	cqe, err := iouring.GetCQE(false)
	fmt.Println(cqe, err)
	data := (*ioData)(unsafe.Pointer(uintptr(cqe.UserData)))
	fmt.Printf("%+v\n", data)
	iov := data.iov[0]
	base := iov.Base
	fmt.Printf("%v\n", base)
	fmt.Printf("%v\n", *base)
	fmt.Printf("%+v\n", uintptr(unsafe.Pointer(base))+1)
	fmt.Println(iov.Len)
	var bs bytes.Buffer
	for i := 0; i < int(iov.Len); i++ {
		fmt.Println(i)
		value := uintptr(unsafe.Pointer(base)) + uintptr(i)
		fmt.Printf("%+v\n", *(*byte)(unsafe.Pointer(value)))
		bs.WriteByte(*(*byte)(unsafe.Pointer(value)))
	}
	fmt.Println("len: ", bs.Len())
	// bs.WriteTo(os.Stdout)
	fmt.Println(bs.String())
}

func main() {
	if len(os.Args) <= 1 {
		fmt.Printf("Usage: %s file1 file2 ...\n", os.Args[0])
	}

	params := iouring_syscall.IOURingParams{}
	iouring, err := io_uring.New(entries, &params)
	if err != nil {
		panic(fmt.Sprintf("new IOURing error: %v", err))
	}
	defer iouring.Close()

	filename := os.Args[1]
	file, err := os.Open(filename)
	if err != nil {
		fmt.Printf("open file error: %v\n", err)
		return
	}

	stat, err := file.Stat()
	if err != nil {
		panic(err)
	}
	size := stat.Size()
	fmt.Println("size: ", size)

	var reads int
	var writes int
	var offset uint64

	i := 0
	hadReads := reads
	for size >= 0 {
		fmt.Println("read count: ", i)
		i++
		thisSize := size
		if reads+writes >= int(entries) {
			break
		}
		if thisSize > blockSize {
			thisSize = blockSize
		} else if thisSize <= 0 {
			break
		}
		ok := queueRead(iouring, uint64(thisSize), offset, int(file.Fd()))
		fmt.Println("queueRead：", ok)
		if !ok {
			break
		}

		size -= thisSize
		offset += uint64(thisSize)
		reads++
		hadReads++
	}

	submitted, err := iouring.Submit()
	fmt.Println(submitted, err)

	fmt.Println("hadReads: ", hadReads)

	i = 0
	var bs bytes.Buffer
	for {
		i++
		fmt.Println(i)
		cqe, err := iouring.GetCQE(false)
		if err != nil {
			fmt.Println(err)
			break
		}
		if cqe == nil {
			break
		}
		fmt.Printf("%+v\n", cqe)
		data := (*ioData)(unsafe.Pointer(uintptr(cqe.UserData)))
		iov := data.iov[0]
		base := iov.Base

		fmt.Printf("%v\n", base)
		fmt.Printf("%v\n", *base)
		fmt.Printf("%+v\n", uintptr(unsafe.Pointer(base))+1)
		fmt.Println(iov.Len)

		for j := 0; j < int(iov.Len); j++ {
			value := uintptr(unsafe.Pointer(base)) + uintptr(j)
			bs.WriteByte(*(*byte)(unsafe.Pointer(value)))
		}
		fmt.Println(bs.Len())
	}
	fmt.Println("len: ", bs.Len())
	bs.WriteTo(os.Stdout)
	fmt.Println(i)
}
